use anyhow::Result;
use clap::Parser;
use log::{info, warn, debug};
use std::path::PathBuf;
use std::process::Command;
use std::time::Duration;
use std::thread;
use std::fs;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    /// 要克隆的仓库 URL
    #[arg(short, long)]
    url: String,

    /// 克隆目标目录
    #[arg(short, long)]
    path: Option<PathBuf>,

    /// 是否显示详细日志
    #[arg(short, long)]
    verbose: bool,

    /// 最大重试次数
    #[arg(short, long, default_value = "3")]
    retries: u32,

    /// Git 缓冲区大小（MB，默认500）
    #[arg(short = 'b', long, default_value = "500")]
    buffer_size: u64,
    
    /// 镜像存储目录（默认为当前目录下的 mirrors）
    #[arg(short = 'm', long)]
    mirrors_dir: Option<PathBuf>,
}

fn run_git_command(args: &[&str], cwd: Option<&PathBuf>) -> Result<()> {
    let mut cmd = Command::new("git");
    cmd.args(args);
    if let Some(dir) = cwd {
        cmd.current_dir(dir);
    }
    
    debug!("执行命令: git {}", args.join(" "));
    
    // 设置输出到标准输出和标准错误
    cmd.stdout(std::process::Stdio::inherit())
        .stderr(std::process::Stdio::inherit());
    
    // 执行命令并等待完成
    let status = cmd.status()?;
    
    if !status.success() {
        return Err(anyhow::anyhow!("Git command failed with status: {}", status));
    }
    
    Ok(())
}

fn setup_git_config(path: &PathBuf, buffer_size: u64) -> Result<()> {
    let buffer_bytes = buffer_size * 1024 * 1024;
    run_git_command(
        &["config", "http.postBuffer", &buffer_bytes.to_string()],
        Some(path)
    )?;
    debug!("设置 Git 配置，缓冲区大小: {}MB", buffer_size);
    Ok(())
}

fn clone_repository(url: &str, path: PathBuf, args: &Args) -> Result<()> {
    debug!("开始克隆仓库: {}", url);
    
    // 检查目标目录是否已存在仓库
    if path.exists() {
        match run_git_command(&["rev-parse", "--git-dir"], Some(&path)) {
            Ok(_) => {
                info!("发现已存在的仓库，跳过克隆");
                return Ok(());
            }
            Err(_) => {
                warn!("目标目录已存在但不是有效的 Git 仓库，将删除");
                fs::remove_dir_all(&path)?;
            }
        }
    }

    // 创建 mirrors 目录
    let mirrors_dir = args.mirrors_dir.clone().unwrap_or_else(|| PathBuf::from("mirrors"));
    if !mirrors_dir.exists() {
        fs::create_dir_all(&mirrors_dir)?;
    }

    // 从 URL 中提取仓库名称
    let url_obj = url::Url::parse(url).expect("Invalid URL");
    let segments = url_obj.path_segments().unwrap().collect::<Vec<_>>();
    let repo_name = segments.last().unwrap().trim_end_matches(".git");
    let mirror_path = mirrors_dir.join(format!("{}.git", repo_name));

    // 执行镜像克隆
    info!("执行镜像克隆到目录: {:?}", mirror_path);
    for attempt in 1..=args.retries {
        info!("尝试镜像克隆 (第 {} 次)", attempt);
        
        match run_git_command(
            &["clone", "--mirror", url, mirror_path.to_str().unwrap()],
            None
        ) {
            Ok(_) => {
                info!("镜像克隆成功");
                break;
            }
            Err(e) => {
                warn!("镜像克隆失败: {}", e);
                if attempt < args.retries {
                    info!("等待 5 秒后重试...");
                    thread::sleep(Duration::from_secs(5));
                } else {
                    return Err(anyhow::anyhow!("镜像克隆失败，已达到最大重试次数"));
                }
            }
        }
    }

    // 设置 Git 配置
    setup_git_config(&mirror_path, args.buffer_size)?;

    // 从镜像克隆到目标目录
    info!("从镜像克隆到目标目录...");
    for attempt in 1..=args.retries {
        info!("尝试从镜像克隆 (第 {} 次)", attempt);
        
        match run_git_command(
            &["clone", mirror_path.to_str().unwrap(), path.to_str().unwrap()],
            None
        ) {
            Ok(_) => {
                info!("从镜像克隆成功");
                break;
            }
            Err(e) => {
                warn!("从镜像克隆失败: {}", e);
                if attempt < args.retries {
                    info!("等待 5 秒后重试...");
                    thread::sleep(Duration::from_secs(5));
                } else {
                    return Err(anyhow::anyhow!("从镜像克隆失败，已达到最大重试次数"));
                }
            }
        }
    }

    // 删除镜像目录
    info!("删除镜像目录...");
    fs::remove_dir_all(&mirror_path)?;
    info!("镜像目录已删除");

    Ok(())
}

fn main() -> Result<()> {
    let args = Args::parse();
    
    // 初始化日志
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(
        if args.verbose { "debug" } else { "info" }
    )).init();

    info!("开始克隆仓库: {}", args.url);
    info!("重试次数: {}", args.retries);
    info!("缓冲区大小: {}MB", args.buffer_size);
    if let Some(ref mirrors_dir) = args.mirrors_dir {
        info!("镜像存储目录: {:?}", mirrors_dir);
    }

    // 获取目标路径
    let path = args.path.clone().unwrap_or_else(|| {
        let url = url::Url::parse(&args.url).expect("Invalid URL");
        let segments = url.path_segments().unwrap().collect::<Vec<_>>();
        let repo_name = segments.last().unwrap().trim_end_matches(".git");
        PathBuf::from(repo_name)
    });

    // 执行克隆
    clone_repository(&args.url, path, &args)?;

    info!("克隆完成！");

    Ok(())
}
